﻿using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using RayD.Core;
using System;
using System.Text.Encodings.Web;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using Microsoft.IdentityModel.Tokens;
using System.IdentityModel.Tokens.Jwt;
using System.Linq;
using System.Text;

namespace RayD.App.Setup
{
    /// <summary>
    /// Jwt 启动服务
    /// </summary>
    public static class JwtSetup
    {
        public static void AddJwtSetup(this IServiceCollection services)
        {
            if (services == null) throw new ArgumentNullException(nameof(services));

            JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();

            /* 参数 */
            var Issuer = AppSettings.Jwt.Issuer;
            var Audience = AppSettings.Jwt.Audience;
            var SigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(AppSettings.Jwt.SigningKey));

            services.AddAuthentication(o =>
            {
                o.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
                o.DefaultChallengeScheme = nameof(ApiResponseHandler);
                o.DefaultForbidScheme = nameof(ApiResponseHandler);
            })
            .AddJwtBearer(options =>
            {
                //令牌验证参数
                options.TokenValidationParameters = new TokenValidationParameters
                {
                    ValidateIssuer = true,//验证发行人
                    ValidIssuer = Issuer,
                    ValidateAudience = true,//验证订阅人
                    ValidAudience = Audience,
                    ValidateIssuerSigningKey = true,//验证发行人的签名密钥
                    IssuerSigningKey = SigningKey,
                    ValidateLifetime = true,//是否验证超时  当设置exp和nbf时有效 同时启用ClockSkew 
                    ClockSkew = TimeSpan.Zero,//注意这是缓冲过期时间，总的有效时间等于这个时间加上jwt的过期时间，如果不配置，默认是5分钟
                    RequireExpirationTime = true //是否要求过期
                    //总的Token有效时间 = expires + ClockSkew;
                };

                options.Events = new JwtBearerEvents
                {
                    OnAuthenticationFailed = context =>
                    {
                        var token = context.Request.Headers["Authorization"].ToString().Replace("Bearer ", "");
                        var jwtToken = (new JwtSecurityTokenHandler()).ReadJwtToken(token);

                        if (jwtToken.Issuer != Issuer)
                        {
                            context.Response.Headers.Add("Token-Error-Iss", "issuer is wrong!");
                        }

                        if (jwtToken.Audiences.FirstOrDefault() != Audience)
                        {
                            context.Response.Headers.Add("Token-Error-Aud", "Audience is wrong!");
                        }

                        // 如果过期，则把<是否过期>添加到，返回头信息中
                        if (context.Exception.GetType() == typeof(SecurityTokenExpiredException))
                        {
                            context.Response.Headers.Add("Token-Expired", "true");
                        }
                        return Task.CompletedTask;
                    }
                };
            })
            .AddScheme<AuthenticationSchemeOptions, ApiResponseHandler>(nameof(ApiResponseHandler), o => { });
        }
    }

    /// <summary>
    /// 授权认证的自定义返回格式
    /// </summary>
    public class ApiResponseHandler : AuthenticationHandler<AuthenticationSchemeOptions>
    {
        public ApiResponseHandler(IOptionsMonitor<AuthenticationSchemeOptions> options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock) : base(options, logger, encoder, clock)
        {
        }

        protected override Task<AuthenticateResult> HandleAuthenticateAsync()
        {
            throw new NotImplementedException();
        }
        protected override async Task HandleChallengeAsync(AuthenticationProperties properties)
        {
            Response.ContentType = "application/json";
            Response.StatusCode = (int)ResponseCode.nologin;
            await Response.WriteAsync(JsonConvert.SerializeObject(new ResParameter() { code = ResponseCode.nologin, msg = "很抱歉，您无权访问该接口，请确保已经登录!", data = null }));
            //清除登录信息（Jwt）
            AuthenticationHelper.Clear();
        }

        protected override async Task HandleForbiddenAsync(AuthenticationProperties properties)
        {
            Response.ContentType = "application/json";
            Response.StatusCode = (int)ResponseCode.unauthroized;
            await Response.WriteAsync(JsonConvert.SerializeObject(new ResParameter() { code = ResponseCode.unauthroized, msg = "很抱歉，您的访问权限等级不够，联系管理员!", data = null }));
        }
    }
}
